/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.google.cse.emea.universaltransformer.transformers;

import com.google.cse.emea.universaltransformer.editors.SimpleTableEditor;
import com.google.cse.emea.jct.utils.Profile;
import com.google.cse.emea.universaltransformer.editors.SimpleTextEditor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;

import java.util.Vector;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import static java.util.logging.Level.*;
import static com.google.cse.emea.universaltransformer.TransformLogger.*;

/**
 *
 * @author jtrapp
 */
public class CSVModule extends AbstractXSLTModule {

    public static final String DELIMITER_SPACES = "SPACES";
    public static final String DELIMITER_TAB = "TAB";
    public static final String DELIMITER_SPACE = "SPACE";
    public static final String CSV_LABEL = "csv";
    private CSVInputPanel inputPanel = null;
    private CSVOutputPanel outputPanel = null;
    private SimpleTableEditor editor = null;
    private Vector<String> inputDelimiterHistory;

    public CSVModule() {
//           setInputDelimiter(INPUT_DELIMITER.getProperty("\t"));
//           inputDelimiterHistory = INPUT_DELIMITER.loadHistory(10);
    }

    @Override
    public String getName() {
        return CSV_LABEL;
    }

    /** Parse the xml file from the archive and generate a
     * dom document for further use. */
    @Override
    public boolean parse(String filename) {
        if (checkChanged(filename)){
        File tmpFile = null;
        try {
            tmpFile = File.createTempFile("com.google.cse.transformer", ".xml");
            String encoding = profile.get(CSVProfileEnum.INPUT_ENCODING);
            boolean selectDelimiterKey = !profile.getBoolean(CSVProfileEnum.INPUT_DELIMITER_AUTO);
            String defaultDelimiter = profile.get(CSVProfileEnum.INPUT_DELIMITER);
            String delimiter = getDelimiter(defaultDelimiter, selectDelimiterKey, encoding, filename);
            inputPanel.updateUI(selectDelimiterKey, delimiter);
            File csvFile = new File(filename);
            toXML(tmpFile, csvFile, delimiter, AbstractXSLTModule.getCharset(encoding));
            log(INFO, "Writen " + tmpFile.getAbsolutePath() + " " + tmpFile.length());
            return super.parse(tmpFile, null, "UTF-8");
        } catch (IOException ioe) {
            log(SEVERE, "Error" + ioe.getMessage());

        }
        clearDocument();
        return false;
        } else {
            return hasDocument();
        }
    }

    /** 
     * Convert the csv file to an xml file 
     */
    public void toXML(File outputFile, File inputFile, String delimiter, Charset inCharset) throws IOException {

        FileInputStream stream = new FileInputStream(inputFile);
        InputStreamReader isr = new InputStreamReader(stream, inCharset);
        BufferedReader reader = new BufferedReader(isr);

        FileOutputStream fos = new FileOutputStream(outputFile);
        Charset outCharset = AbstractXSLTModule.getCharset("UTF-8");
        OutputStreamWriter osw = new OutputStreamWriter(fos, outCharset);
        PrintWriter writer = new PrintWriter(osw);
        writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        writer.println("<table>");

        String line = reader.readLine();
        int i = 0;
        if (line != null) {
            String[] columnNames = line.split(delimiter);

            while ((line = reader.readLine()) != null) {
                i++;
                writer.println("<row index=\"" + i + "\">");
                String[] cellsInRow = line.split(delimiter);
                for (int jj = 0; jj < cellsInRow.length; jj++) {
                    writer.println("<cell index=\"" + jj + "\" name=\"" + columnNames[jj] + "\">" + encode(cellsInRow[jj]) + "</cell>");
                //log("<cell index=\""+jj+"\" name=\""+columnNames[jj]+">"+cellsInRow[jj]+"</cell>");
                }
                writer.println("</row>");
            }
        }
        writer.println("</table>");
        writer.close();
        fos.close();
        reader.close();
    }

    /**
     * Get the delimiter that is used when parsing the table columns.
     * @param delimiterKey Preferences key for the delimiter
     * @param selectDelimiterKey Preferences key, to determine, if the delimite should be retreved automatically
     */
    public static String getDelimiter(String defaultDelimiter,
            boolean selectDelimiterKey,
            String encoding,
            String filename) {
        String delimiter = null;
        if ((filename != null) && (filename.length() != 0) && (!selectDelimiterKey)) {
            delimiter = guessDelimiter(filename, encoding);
        }
        if ((delimiter == null) || (delimiter.length() == 0)) {
            delimiter = defaultDelimiter;
        }
        if (delimiter==null||delimiter.equalsIgnoreCase(DELIMITER_TAB)) {
            delimiter = "\t";
        } else if (delimiter.equalsIgnoreCase(DELIMITER_SPACE)) {
            delimiter = " ";
        } else if (delimiter.equalsIgnoreCase(DELIMITER_SPACES)) {
            delimiter = "[ ]*";
        }

        return delimiter;
    }

    private String encode(String string) {
        return string.replace("&", "&amp;").replace("<", "&lt;").replace("\"", "&quot;");
    }

    /** 
     * Get a visible representation for the delimiter for special characters
     * @param delimiter
     * @return
     */
    public String getVisibleDelimiter(String delimiter) {
        if (delimiter.equals("/t")) {
            return DELIMITER_TAB;
        } else if (delimiter.equals(" ")) {
            return DELIMITER_SPACE;
        } else if (delimiter.matches("\\s*")) {
            return DELIMITER_SPACES;
        }
        return delimiter;
    }

    /**
     * Set the delimiterfor the file and update the UI (only applicable, if the type is csv).
     */
    private void setInputDelimiter(String delimiter) {
        String visibleDelimiter = getVisibleDelimiter(delimiter);

        if (inputPanel != null) {
            inputPanel.setInputDelimiter(delimiter);
        }
        profile.updateHistory(
                CSVProfileEnum.INPUT_DELIMITER, inputDelimiterHistory,
                visibleDelimiter);
    }

    /**
     * 
     * @return
     */
    public SimpleTableEditor getInputEditor() {
        if (editor == null) {
            editor = new SimpleTableEditor();
        }

        return editor;
    }


    @Override
    public JPanel getInputPanel() {
        if (inputPanel == null) {
            ActionListener delimiterChanged = new ActionListener() {

                public void actionPerformed(ActionEvent e) {
                    if (e.getSource() instanceof JComboBox) {
                        JComboBox combobox = (JComboBox) e.getSource();
                        setInputDelimiter(combobox.getSelectedItem().toString());
                    }
                }
            };
            ActionListener enterDelimiterChanged = new ActionListener() {

                public void actionPerformed(ActionEvent e) {
                    if (e.getSource() instanceof JCheckBox) {
                        boolean autoDelimiter = !((JCheckBox) e.getSource()).isSelected();
                        if (profile != null) {
                            profile.put(CSVProfileEnum.INPUT_DELIMITER_AUTO.getName(), autoDelimiter);

                            String filename = profile.get(CSVProfileEnum.INPUT_FILE);
                            String encoding = profile.get(CSVProfileEnum.INPUT_ENCODING);
                            String defaultDelimiter = profile.get(CSVProfileEnum.INPUT_DELIMITER);
                            String delimiter = getDelimiter(defaultDelimiter, !autoDelimiter, encoding, filename);
                            
                            inputPanel.updateUI(!autoDelimiter, getVisibleDelimiter(delimiter));
                        }
                    }
                }
            };


            inputPanel = new CSVInputPanel(delimiterChanged, enterDelimiterChanged);
        }



        return inputPanel;
    }

    /**Count character occurances in string*/
    public static int count(String line, char c) {
        int count = 0;
        int index = 0;
        while ((index = line.indexOf(c, index) + 1) > 0) {
            count++;
        }
        return count;
    }

    /** Try to determine the delimiter automatically */
    public static String guessDelimiter(String file, String encoding) {
        try {
            FileInputStream inStream = new FileInputStream(file);

            Charset incharset = getCharset(encoding);
            BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, incharset));
            String line = reader.readLine();
            if (line != null) {
                char[] delimiters = {'\t', ',', ';'};
                int maxIndex = 0;
                int maxCount = 0;
                for (int i = 0; i < delimiters.length; i++) {
                    int current = count(line, delimiters[i]);
                    if (maxCount < current) {
                        maxIndex = i;
                        maxCount = current;
                    }
                }

                if (maxCount > 0) {
                    log(INFO, "Delimiter:" + delimiters[maxIndex]);
                    return delimiters[maxIndex] + "";
                }
            }
        } catch (IOException ioe) {
            log(WARNING, "Error while trying to retrieve delimiter. " + ioe.getMessage());
        }
        log(WARNING, "Delimiter null");
        return null;
    }

    @Override
    public JPanel getOutputPanel() {
        if (outputPanel == null) {
            outputPanel = new CSVOutputPanel();
        }

        return outputPanel;
    }


    @Override
    public void setProfile(Profile profile) {
        super.setProfile(profile);
        String inputFile = profile.get(CSVProfileEnum.INPUT_FILE);
        if ((inputFile != null) && inputFile.length() > 0) {
            File file = new File(inputFile);
            if (file.exists() && file.isFile()) {
                getInputPanel().setEnabled(true);
            } else {
                getInputPanel().setEnabled(false);
            }

        }
        boolean selectDelimiter = !profile.getBoolean(CSVProfileEnum.INPUT_DELIMITER_AUTO);
        String delimiter = profile.get(CSVProfileEnum.INPUT_DELIMITER);
        inputPanel.updateUI(selectDelimiter, delimiter);

    }
}
